package com.jf.nep.render;

import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL30;

public class MeshStoreImpl implements MeshStore {
    private Map<String, MeshAllocation> allocations = new HashMap<String, MeshAllocation>();

    public void createMesh(Mesh mesh) {
        MeshAllocation allocation = new MeshAllocation();
        allocations.put(mesh.id(), allocation);
        
        // Create vertex buffer
        FloatBuffer verticesBuffer = BufferUtils.createFloatBuffer(mesh.getVertices().length *
                Vertex.TOTAL_COUNT);
        
        for (Vertex vertex : mesh.getVertices()) {
            // Add position, color and texture floats to the buffer
            verticesBuffer.put(vertex.getArray());
        }        
        verticesBuffer.flip();
        
        // Create index buffer
        allocation.indicesCount = mesh.getIndices().length;
        ByteBuffer indicesBuffer = BufferUtils.createByteBuffer(allocation.indicesCount);
        indicesBuffer.put(mesh.getIndices());
        indicesBuffer.flip();
        
        // Create vertex array object
        allocation.vaoId = GL30.glGenVertexArrays();
        GL30.glBindVertexArray(allocation.vaoId);
        
        // Create a new Vertex Buffer Object in memory and select it (bind)
        allocation.vboId = GL15.glGenBuffers();
        GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, allocation.vboId);
        GL15.glBufferData(GL15.GL_ARRAY_BUFFER, verticesBuffer, GL15.GL_STATIC_DRAW);
        
        for (int i = 0; i < Vertex.NUMBER_OF_ELEMENT_TYPES; i++) {
            GL20.glVertexAttribPointer(i, Vertex.ELEMENT_COUNTS[i], GL11.GL_FLOAT, 
                    false, Vertex.BYTE_SIZE, Vertex.BYTE_OFFSETS[i]);
        }
        GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);

        // Deselect (bind to 0) the VAO
        GL30.glBindVertexArray(0);
        
        // Create a new VBO for the indices and select it (bind) - INDICES
        allocation.vboiId = GL15.glGenBuffers();
        GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, allocation.vboiId);
        GL15.glBufferData(GL15.GL_ELEMENT_ARRAY_BUFFER, indicesBuffer, GL15.GL_STATIC_DRAW);
        GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
        
    }
    
    public void destroyMesh(String meshId) {
        MeshAllocation allocation = allocations.get(meshId);
        
        // Select the VAO
        GL30.glBindVertexArray(allocation.vaoId);

        // Disable the VBO index from the VAO attributes list
        for (int i = 0 ; i < Vertex.NUMBER_OF_ELEMENT_TYPES; i++) {
            GL20.glDisableVertexAttribArray(i);
        }

        // Delete the vertex VBO
        GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
        GL15.glDeleteBuffers(allocation.vboId);

        // Delete the index VBO
        GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
        GL15.glDeleteBuffers(allocation.vboiId);

        // Delete the VAO
        GL30.glBindVertexArray(0);
        GL30.glDeleteVertexArrays(allocation.vaoId);
        
        allocations.remove(allocation);
    }
    
    public MeshAllocation getAllocation(String meshId) {
        return allocations.get(meshId);
    }

    @Override
    public void destroyAll() {
        Iterator<String> it = allocations.keySet().iterator();
        while (it.hasNext()) {
            destroyMesh(it.next());
        }
        
    }
}
